///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IO_CBUFFEREDLINEREADER_H_
#define _BUOLA_IO_CBUFFEREDLINEREADER_H_

#include <buola/buola.h>
#include <buola/container/ccircularbuffer.h>

namespace buola { namespace io {

template<int pMaxLen=1024>
class CBufferedLineReader
{
public:
    CBufferedLineReader(char pEndLine='\n',char pIgnore='\r')
        :   mCheckLen(0)
        ,   mEndLine(pEndLine)
        ,   mIgnore(pIgnore)
        ,   mEOF(false)
    {}

private:
    bool CheckLine(std::string &pString)
    {
        while(mCheckLen<mBuffer.GetSize())
        {
            if(mBuffer[mCheckLen]==mEndLine)
            {
                pString.resize(mCheckLen);
                mBuffer.PopData(pString.data(),mCheckLen);
                mBuffer.ConsumeData(mCheckLen+1);

                if(mCheckLen&&pString[mCheckLen-1]==mIgnore)
                    pString.resize(mCheckLen-1);

                mCheckLen=0;
                return true;
            }
            mCheckLen++;
        }
        return false;
    }

public:
    template<typename tDevice>
    bool GetLine(std::string &pString,tDevice &pDevice,bool pBlock=false)
    {
        while(!CheckLine(pString))
        {
            if(!mBuffer.GetFreeCapacity())
                throw XFull("buffer full in CBufferedLineReader");
            auto lBuffer=mBuffer.GetFillBuffer();
            int lRead=pBlock?pDevice.Receive(lBuffer.first,lBuffer.second)
                            :pDevice.ReceiveNB(lBuffer.first,lBuffer.second);
            if(lRead<0) return false;
            if(lRead==0)
            {
                mEOF=true;
                return false;
            }

            mBuffer.FillData(lRead);

            if(lRead<lBuffer.second)
            {
                if(!pBlock)
                    return CheckLine(pString);
                else
                {
                    bool lResult=CheckLine(pString);
                    if(lResult) return true;
                }
            }
        }

        return true;
    }

    bool AtEOF()  {   return mEOF;    }

private:
    CCircularBuffer<char,pMaxLen> mBuffer;
    int mCheckLen;  //number of checked bytes
    char mEndLine;
    char mIgnore;
    bool mEOF;
};

/*namespace io*/ } /*namespace buola*/ }

#endif
